<template>
  <view class="select-container" v-show="show" @touchmove.stop.prevent>
    <view
      class="mask"
      :class="activeClass ? 'mask-show' : ''"
      @tap="onCancel(true)"
    ></view>
    <view class="select-box" :class="activeClass ? 'select-box-show' : ''">
      <view class="header">
        <text class="cancel" @tap="onCancel">{{ cancelText }}</text>
        <view class="all" @tap="onAllToggle" v-if="allShow">
          <text :class="isAll ? 'all-active' : ''">全选</text>
        </view>
        <text class="confirm" @tap="onConfirm">{{ confirmText }}</text>
      </view>
      <view class="body-warp">
        <scroll-view class="body" scroll-y="true">
          <slot v-if="!data.length" name="tips">
            <view class="empty-tips">暂无数据~</view>
          </slot>
          <view
            class="select-item"
            :class="[
              item.disabled ? 'disabled' : '',
              selectedArr[index] ? 'selected' : '',
            ]"
            v-for="(item, index) in data"
            :key="item[valueName]"
            @tap="onSelected(index)"
          >
            <view class="label">{{ item[labelName] }}</view>
            <text v-show="selectedArr[index]" class="selected-icon">✔</text>
          </view>
        </scroll-view>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref, computed, watch, nextTick, onMounted } from 'vue';

// 定义组件props
const props = defineProps({
  // 双向绑定控制显示/隐藏
  modelValue: {
    type: Boolean,
    default: false,
  },
  // 取消按钮文字
  cancelText: {
    type: String,
    default: '取消',
  },
  // 确认按钮文字
  confirmText: {
    type: String,
    default: '确认',
  },
  // label对应的key名称
  labelName: {
    type: String,
    default: 'label',
  },
  // value对应的key名称
  valueName: {
    type: String,
    default: 'value',
  },
  // 是否允许点击遮罩层关闭
  maskCloseAble: {
    type: Boolean,
    default: true,
  },
  // 是否显示全选
  allShow: {
    type: Boolean,
    default: true,
  },
  // 模式 (multiple/single)
  mode: {
    type: String,
    default: 'multiple',
  },
  // 默认选中值
  defaultSelected: {
    type: Array,
    default: () => [],
  },
  // 数据源
  data: {
    type: Array,
    required: true,
    default: () => [],
  },
});

// 定义组件事件
const emit = defineEmits(['update:modelValue', 'change', 'cancel', 'confirm']);

// 响应式变量
const show = ref(false);
const activeClass = ref(false);
const selectedArr = ref([]);
const selectedArrOld = ref([]);

// 计算属性 - 是否全选
const isAll = computed(() => {
  const wipeDisabledList = returnWipeDisabledList();
  if (!wipeDisabledList.length) return false;
  return !wipeDisabledList.includes(false);
});

// 方法定义
// 设置默认选中状态
const setItemActiveState = () => {
  if (props.data.length && props.defaultSelected.length) {
    props.data.forEach((item, i) => {
      for (let n = 0; n < props.defaultSelected.length; n++) {
        if (
          !item.disabled &&
          item[props.valueName] === props.defaultSelected[n]
        ) {
          selectedArr.value.splice(i, 1, true);
          break;
        }
      }
    });
  }
};

// 选择事件
const onSelected = (index) => {
  if (props.data[index].disabled) return;
  // 单选模式下先清空已选项
  if (props.mode !== 'multiple') {
    selectedArr.value = selectedArr.value.map(() => false);
  }
  selectedArr.value.splice(index, 1, !selectedArr.value[index]);
};

// 取消事件
const onCancel = (isMask = false) => {
  if (!isMask || props.maskCloseAble) {
    show.value = false;
    selectedArr.value = JSON.parse(JSON.stringify(selectedArrOld.value));
  } else {
    return;
  }
  emit('cancel');
};

// 返回去除disabled状态后的对照列表
const returnWipeDisabledList = () => {
  const arr = [];
  selectedArr.value.forEach((el, index) => {
    if (!props.data[index].disabled) arr.push(el);
  });
  return arr;
};

// 全选/取消全选事件
const onAllToggle = () => {
  const wipeDisabledList = returnWipeDisabledList();
  // 判断当前是否全选
  const isCheckAll = !wipeDisabledList.includes(false);

  selectedArr.value.forEach((_, index) => {
    if (!props.data[index].disabled) {
      selectedArr.value.splice(index, 1, !isCheckAll);
    }
  });
};

// 确认事件
const onConfirm = () => {
  show.value = false;
  const selectedData = [];
  selectedArr.value.forEach((el, index) => {
    if (el) {
      selectedData.push(props.data[index]);
    }
  });

  if (props.mode === 'multiple') {
    emit('confirm', selectedData);
  } else {
    const backData = selectedData[0] || {};
    emit('confirm', backData);
  }
};

// 监听与生命周期
// 监听modelValue变化控制显示状态
watch(
  () => props.modelValue,
  async (newVal) => {
    show.value = newVal;
    await nextTick();
    activeClass.value = newVal;
    if (newVal) {
      // 保存当前选中状态快照
      selectedArrOld.value = JSON.parse(JSON.stringify(selectedArr.value));
    }
  },
  { immediate: true },
);

// 监听show变化同步到父组件
watch(
  () => show.value,
  (newVal) => {
    emit('update:modelValue', newVal);
    emit('change', newVal);
  },
);

// 监听数据源变化重置选中状态
watch(
  () => props.data,
  (list) => {
    selectedArr.value = list.map(() => false);
    setItemActiveState();
  },
  { deep: true, immediate: true },
);

// 监听默认选中值变化
watch(
  () => props.defaultSelected,
  () => {
    setItemActiveState();
  },
  { deep: true },
);

// 组件挂载时初始化
onMounted(() => {
  show.value = props.modelValue;
});
</script>

<style lang="scss" scoped>
.select-container {
  width: 100vw;
  height: 100vh;
  position: fixed;
  left: 0;
  top: 0;
  z-index: 999;
  $paddingLR: 18rpx;

  .mask {
    width: 100%;
    height: 100%;
    background-color: $uni-bg-color-mask;
    opacity: 0;
    transition: opacity 0.3s;

    &.mask-show {
      opacity: 1;
    }
  }

  // 选择器内容区域
  .select-box {
    width: 100%;
    position: absolute;
    bottom: 0;
    left: 0;
    transform: translate3d(0px, 100%, 0px);
    background-color: $uni-bg-color;
    transition: all 0.3s;

    &.select-box-show {
      transform: translateZ(0);
    }

    .header {
      display: flex;
      box-sizing: border-box;
      width: 100%;
      justify-content: space-between;
      border-bottom: 1px solid $uni-border-color;
      line-height: 76rpx;
      font-size: 30rpx;
      padding: 0 $paddingLR;

      .cancel {
        color: $uni-text-color-grey;
      }

      .all {
        color: $uni-color-success;

        .all-active {
          &::after {
            display: inline-block;
            content: '✔';
            padding-left: 8rpx;
          }
        }
      }

      .confirm {
        color: $uni-color-primary;
      }
    }

    .body-warp {
      width: 100%;
      height: 30vh;
      box-sizing: border-box;
      padding: 20rpx $paddingLR;
    }

    .body {
      width: 100%;
      height: 100%;
      overflow-y: auto;

      .empty-tips {
        margin-top: 25%;
        text-align: center;
        font-size: 26rpx;
      }

      .select-item {
        display: flex;
        font-size: 26rpx;
        line-height: 58rpx;
        color: #303133;
        position: relative;
        transition: all 0.3s;

        &.selected {
          color: $uni-color-primary;
        }

        &.disabled {
          color: $uni-text-color-disable;
        }

        > .label {
          flex: 1;
          text-align: center;
        }

        > .selected-icon {
          position: absolute;
          right: 0;
          top: 50%;
          transform: translateY(-50%);
        }
      }
    }
  }
}
</style>
